#!/usr/bin/env ruby

require 'find'
require 'ftools'
require 'optparse'
require 'ostruct'
require 'fileutils'

def final_cleanup(count, options)
  # cleanup any messed up files
  Find.find(options.dest_dir) do |path|
    if FileTest.file?(path)
      if File.extname(path) == '.mp3'
        if File.zero?(path)
          FileUtils.rm(path, :verbose => true)
        end
      end
    end
  end
  
  # Output number of files copied
  puts count.to_s + " Files Copied"

  if count > 0
    # normalize files
    if options.normalize
      mp3gainCommand = 'mp3gain -T -r -p '
      if !options.verbose
        mp3gainCommand += '-q '
    end
      mp3gainCommand += options.dest_dir + '*'
      
      system(mp3gainCommand)
    end
  end
  
end

options = OpenStruct.new
options.verbose = false
options.clear = false
options.force = false
options.normalize = false
options.excludes =[]
options.num_files = 0


opts = OptionParser.new do |opts|
  opts.banner = 'Usage: fill_flash_drive.rb [options]'
  opts.separator ""
  opts.separator "This program fills your flash drive with mp3s"
  opts.separator ""
  opts.separator "Choose either -f to fill the drive,"
  opts.separator "or -n [num] to put [num] songs on the drive"


  opts.on('-n','--num [num]', 'Number of Files to Copy') do |o|
    options.num_files = o
  end

  opts.on('-f','--fill', 'Fill drive until full') do |o|
    options.num_files = 1000000000
  end

  opts.separator ""
  opts.separator "These options are required:"

  opts.on('-s','--srcdir [dir]', 'Source Directory of Files') do |s|
    options.src_dir = s
  end

  opts.on('-d','--destdir [dir]', 'Destination Directory of Files') do |d|
    options.dest_dir = d
  end

  opts.separator ""
  opts.separator "These options are optional:"

  opts.on('-F','--force', 'Don''t prompt before clearing drive') do |f|
    options.force = f
  end

  opts.on('-v', '--verbose', 'Run Verbosely') do |v|
    options.verbose = v
  end

  opts.on('-c', '--clear', 'Clear Destination Directory before filling') do |c|
    options.clear = c
  end

  opts.on('-m', '--mp3gain', 'Run mp3gain on drive to normalize volume') do |m|
    options.normalize = m
  end

  opts.on('-e', '--exclude [values]', 'Comma seperated list of values to exclude') do |e|
    options.excludes = e.split(',')
  end

  opts.on_tail('-h', '--help', 'Show this message') do |h|
    puts opts
    exit 0
  end
end

opts.parse!(ARGV)

if options.src_dir == nil
  puts opts
  puts
  puts '*** You must supply a source directory with -s! ***'
  exit 1
end

if options.dest_dir == nil
  puts opts
  puts
  puts '*** You must supply a destination directory with -d! ***'
  exit 1
end

options.src_dir = options.src_dir.insert(-1,'/')
options.dest_dir = options.dest_dir.insert(-1,'/')

# Clear dest dir before filling
if options.clear
  really_clear = 'y'
  
  # If force isn't selected, give user a chance to back out
  # and/or verify that the path they want to clear is correct
  if !options.force
    puts 'Are you sure you want to delete all mp3s from ' + options.dest_dir + '? (y/n)'
    really_clear = gets.chomp
  end
  
  # if they are sure, or did force, then clear dest_dir
  if really_clear[0,1].downcase == 'y'
    if options.verbose
      puts 'Clearing ' + options.dest_dir
    end
    FileUtils.rm(Dir.glob(options.dest_dir + '*.mp3'), :verbose => options.verbose)
    puts options.dest_dir + ' Cleared! Filling drive...'
  else
    puts 'Clearing Skipped! filling drive...'
  end
end

mp3s = Array.new()
count = 0

# read in all available mp3s and put paths into array
Find.find(options.src_dir) do |path|

  # if there is a file there
  if FileTest.file?(path)
    #if it is an mp3 file and it is not of zero size
    if File.extname(path) == '.mp3' and !File.zero?(path)
      
      excludeFile = false
      # iterate through excludes to make sure it is okay
      options.excludes.each do |ex|
        # only bother looking for another exclude in this path if we
        # haven't already excluded it.
        if excludeFile == false  
          # If the exclude is found in the path, then exclude it
          excludeFile = path.include? ex
        end
      end
      
      if excludeFile
        puts 'Excluding ' + path
        Find.prune()
      else
        mp3s.push(path)
      end
    end
  end
end

# randomize mp3s
random_mp3s = mp3s.sort_by {rand}

# while we are less than the number of files 
# fill dest_dir from array.  Watch for exceptions (full drive,
# invalid permissions, etc.)  To do a fill, we just make num_files 
# so high that it is never reached and wait for the exception of the drive being full.

if !random_mp3s.empty?
  while count < options.num_files.to_i
    begin
      File.copy(random_mp3s[count], options.dest_dir + File.basename(random_mp3s[count]), verbose = options.verbose) 
    rescue Exception, SystemCallError => e
      
      final_cleanup(count, options)
      
      # Output error
      puts e
      
      exit 1
    end
    
    count +=1
  end
end

final_cleanup(count, options)

